package com.hyx.api.utils;


import com.google.common.collect.Maps;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentMap;

/**
 * @Description 时间工具类
 * @ClassName DateUtil
 * @Author zhoubo
 * @CreateDate 2018年6月27
 * @Version 1.0
 */
public class DateUtil {

    /**
     * 小时转换为天的进制
     */
    public static final long TIME_HOUR_TO_DAY = 24;

    /**
     * 分钟转换为小时的进制
     */
    public static final long TIME_MINUTE_TO_HOUR = 60;

    /**
     * 秒转换为分钟的进制
     */
    public static final long TIME_SECS_TO_MINUTE = 60;

    /**
     * 毫秒转换为秒的进制
     */
    public static final long TIME_MSEC_TO_SECS = 1000;

    /**
     * 完整时间 yyyy-MM-dd HH:mm:ss
     */
    public static final String FORMAT_YMD_HMS = "yyyy-MM-dd HH:mm:ss";

    /**
     * 年-月-日 小时:分钟 yyyy-MM-dd HH:mm
     */
    public static final String FORMAT_YMD_HM = "yyyy-MM-dd HH:mm";

    /**
     * 年-月-日 小时 yyyy-MM-dd HH
     */
    public static final String FORMAT_YMD_H = "yyyy-MM-dd HH";

    /**
     * 年-月-日 yyyy-MM-dd
     */
    public static final String FORMAT_YMD = "yyyy-MM-dd";

    /**
     * 年-月 yyyy-MM
     */
    public static final String FORMAT_YM = "yyyy-MM";

    /**
     * 时:分:秒 HH:mm:ss
     */
    public static final String FORMAT_HMS = "HH:mm:ss";

    /**
     * 年月日(中文) yyyy年MM月dd日
     */
    public static final String FORMAT_CHINESE_YMD = "yyyy年MM月dd日";

    /**
     * 年月日(无下划线) yyyyMMdd
     */
    public static final String FORMAT_NOLINE_YMD = "yyyyMMdd";

    /**
     * 时:分 HH:mm
     */
    public static final String FORMAT_NOLINE_HMS = "HHmmss";

    /**
     * 年月日时分秒(无下划线) yyyyMMddHHmmss
     */
    public static final String FORMAT_NOLINE_YMD_HMS = "yyyyMMddHHmmss";

    /**
     * 年月日时分秒毫秒(无下划线) yyyyMMddHHmmssS
     */
    public static final String FORMAT_NOLINE_YMD_HMSS = "yyyyMMddHHmmssSSS";

    /**
     * 年月日(无下划线)时分秒隔开
     */
    public static final String FORMAT_NOLINE_YMD_HMS1 = "yyyyMMdd HH:mm:ss";

    /**
     * 年月日时分秒(点隔开)
     */
    public static final String FORMAT_YMD_LITTLE = "yyyy.MM.dd";

    public static final String FORMAT_YMD_HM_LITTLE = "yyyy.MM.dd HH:mm";

    public static final String FORMAT_YMD_HMS_LITTLE = "yyyy.MM.dd HH:mm:ss";

    /**
     * 年月日时分秒(斜杠隔开)
     */
    public static final String FORMAT_SLANT_YMD = "yyyy/MM/dd";

    public static final String FORMAT_SLANT_YMD_HMS = "yyyy/MM/dd HH:mm:ss";

    /**
     * 日期格式化工具类, DateFormat的缓存容器
     * 减少初始化DateFormat的耗时，增加工具类性能
     */
    private static final ThreadLocal<ConcurrentMap<String, DateFormat>> FORMATER_CACHE =
            ThreadLocal.withInitial(Maps::newConcurrentMap);

    /**
     * 获取缓存格式对象
     *
     * @param format 时间格式字符串
     * @return 格式对象
     */
    private static DateFormat getFormat(String format) {
        return FORMATER_CACHE.get().computeIfAbsent(format, SimpleDateFormat::new);
    }

    /**
     * 获取当前时间
     *
     * @return 返回日期时间
     */
    public static Date now() {
        return localDateTimeToDate(LocalDateTime.now());
    }

    /**
     * 格式化时间为指定格式字符串
     *
     * @param date   时间
     * @param format 格式
     * @return 字符串
     */
    public static String dateToStr(Date date, String format) {
        if (date == null) return "";
        return getFormat(format).format(date);
    }

    /**
     * 将时间格式字符串转换成时间
     *
     * @param dateStr 时间字符串
     * @param format  格式字符串
     * @return 返回日期时间
     * @throws ParseException 转换错误异常
     */
    public static Date strToDate(String dateStr, String format) throws ParseException {
        if (dateStr == null || dateStr.trim().isEmpty()) return null;
        return getFormat(format).parse(dateStr.trim());
    }

    /**
     * 格式化时间为 yyyy-MM-dd HH:mm:ss 格式字符串
     *
     * @param date 时间
     * @return 字符串
     */
    public static String dateToStrYmdhms(Date date) {
        if (date == null) {
            return "";
        }
        return getFormat(FORMAT_YMD_HMS).format(date);
    }

    /**
     * 将时间格式 yyyy-MM-dd HH:mm:ss 字符串转换成时间
     *
     * @param dateStrYmdhms 时间格式字符串
     * @return 返回日期时间
     * @throws ParseException 转换错误异常
     */
    public static Date strToDateYmdhms(String dateStrYmdhms) throws ParseException {
        if (dateStrYmdhms == null) {
            return null;
        }
        return getFormat(FORMAT_YMD_HMS).parse(dateStrYmdhms);
    }

    /**
     * 格式化时间为 yyyy-MM-dd HH:mm 格式字符串
     *
     * @param date 时间
     * @return 字符串
     */
    public static String dateToStrYmdhm(Date date) {
        if (date == null) {
            return "";
        }
        return getFormat(FORMAT_YMD_HM).format(date);
    }

    /**
     * 将时间格式 yyyy-MM-dd HH:mm 字符串转换成时间
     *
     * @param dateStrYmdhm 时间格式字符串
     * @return 返回日期时间
     * @throws ParseException 转换错误异常
     */
    public static Date strToDateYmdhm(String dateStrYmdhm) throws ParseException {
        if (dateStrYmdhm == null || dateStrYmdhm.trim().length() == 0) {
            return null;
        }
        return getFormat(FORMAT_YMD_HM).parse(dateStrYmdhm.trim());
    }

    /**
     * 格式化时间为 yyyy-MM-dd HH 格式字符串
     *
     * @param date 时间
     * @return 字符串
     */
    public static String dateToStrYmdh(Date date) {
        if (date == null) {
            return "";
        }
        return getFormat(FORMAT_YMD_H).format(date);
    }

    /**
     * 将时间格式 yyyy-MM-dd HH 字符串转换成时间
     *
     * @param dateStrYmdh 时间格式字符串
     * @return 返回日期时间
     * @throws ParseException 转换错误异常
     */
    public static Date strToDateYmdh(String dateStrYmdh) throws ParseException {
        if (dateStrYmdh == null || dateStrYmdh.trim().length() == 0) {
            return null;
        }
        return getFormat(FORMAT_YMD_H).parse(dateStrYmdh.trim());
    }

    /**
     * 格式化时间为 yyyy-MM-dd 格式字符串
     *
     * @param date 时间
     * @return 格式时间字符串
     */
    public static String dateToStrYmd(Date date) {
        if (date == null) {
            return "";
        }
        return getFormat(FORMAT_YMD).format(date);
    }

    /**
     * 将时间格式 yyyy-mm-dd 字符串转换成时间
     *
     * @param dateStrYmd 时间格式字符串
     * @return 时间
     */
    public static Date strToDateYmd(String dateStrYmd) throws ParseException {
        if (dateStrYmd == null) {
            return null;
        }
        return getFormat(FORMAT_YMD).parse(dateStrYmd);
    }


    /**
     * 格式化时间为 yyyy-MM 格式字符串
     *
     * @param date 时间
     * @return 字符串
     */
    public static String dateToStrYm(Date date) {
        if (date == null) {
            return "";
        }
        return getFormat(FORMAT_YM).format(date);
    }

    /**
     * 将时间格式字符串 yyyy-mm 转换为日期时间格式
     *
     * @param dateStrYm 时间格式字符串yyyy-MM-dd
     * @return 返回日期时间
     * @throws ParseException 转换出错
     */
    public static Date strToDateYm(String dateStrYm) throws ParseException {
        if (dateStrYm == null) {
            return null;
        }
        return getFormat(FORMAT_YM).parse(dateStrYm);
    }

    /**
     * 格式化时间为 HH:mm:ss 格式字符串
     *
     * @param date 时间
     * @return 返回时分秒字符串
     */
    public static String dateToStrHms(Date date) {
        if (date == null) {
            return "";
        }
        return getFormat(FORMAT_HMS).format(date);
    }

    /**
     * 转换时间为 yyyy年MM月dd日 格式字符串
     *
     * @param date 时间
     * @return 返回时间格式字符串
     */
    public static String dateToChineseStrYmd(Date date) {
        if (date == null) {
            return "";
        }
        return getFormat(FORMAT_CHINESE_YMD).format(date);
    }

    /**
     * 转换时间格式yyyy-MM-dd 字符串 为 yyyy年MM月dd日 格式字符串
     *
     * @param dateStrYmd 时间格式字符串
     * @return 返回格式化时间字符串
     */
    public static String strToChineseStrYmd(String dateStrYmd) throws ParseException {
        if (dateStrYmd == null) {
            return "";
        }
        return getFormat(FORMAT_CHINESE_YMD).format(strToDateYmd(dateStrYmd));
    }

    /**
     * 格式化时间日期为 yyyyMMdd 字符串格式
     *
     * @param Date 时间
     * @return 返回时间格式字符串 yyyyMMdd
     */
    public static String dateToNoLineStrYmd(Date Date) {
        if (Date == null) {
            return "";
        }
        return getFormat(FORMAT_NOLINE_YMD).format(Date);
    }

    /**
     * 格式化时间日期为 yyyyMMddHHmmss 字符串格式
     *
     * @param Date 时间
     * @return 返回时间格式字符串 yyyyMMddHHmmss
     */
    public static String dateToNoLineStrYmdhms(Date Date) {
        if (Date == null) {
            return "";
        }
        return getFormat(FORMAT_NOLINE_YMD_HMS).format(Date);
    }

    /**
     * 格式化时间日期为 yyyyMMddHHmmssS 字符串格式
     *
     * @param Date 时间
     * @return 返回时间格式字符串 yyyyMMddHHmmssS
     */
    public static String dateToNoLineStrYmdhmsS(Date Date) {
        if (Date == null) {
            return "";
        }
        return getFormat(FORMAT_NOLINE_YMD_HMSS).format(Date);
    }

    /**
     * 格式化时间日期为 yyyyMMdd 字符串格式
     *
     * @param dateStrYmd 时间(yyyyMMdd)
     * @return 时间日期
     * @throws ParseException 转换出错
     */
    public static Date strToNoLineDateYmd(String dateStrYmd) throws ParseException {
        if (dateStrYmd == null) {
            return null;
        }
        return getFormat(FORMAT_NOLINE_YMD).parse(dateStrYmd);
    }


    /**
     * 计算日期格式 yyyy-MM-dd 字符串的时间差值
     *
     * @param beforeStrYmd 开始时间字符串
     * @param afterStrYmd  结束时间字符串
     * @return long（天数）
     */
    public static long calculateDecreaseDays(String beforeStrYmd, String afterStrYmd)
            throws ParseException {
        Date beforeDate = getFormat(FORMAT_YMD).parse(beforeStrYmd);
        Date afterDate = getFormat(FORMAT_YMD).parse(afterStrYmd);

        /* 取得距离 1970年1月1日 00:00:00 GMT 的毫秒数 */
        long decrease = (beforeDate.getTime() - afterDate.getTime());

        return decrease / TIME_MSEC_TO_SECS /
                TIME_SECS_TO_MINUTE / TIME_MINUTE_TO_HOUR / TIME_HOUR_TO_DAY;
    }

    /**
     * 获取输入日期的相差时间
     *
     * @param date      输入时间
     * @param fieldType 相差时间类型 例：Calendar.YEAR
     * @param differNum 相差数量
     * @return 返回相差时间 <p> LocalDateTime localDateTime = dateToLocalDateTime(date); Duration duration
     * = Duration.of(differNum, unit); localDateTimeToDate(localDateTime.plus(duration))
     */
    public static Date differDate(Date date, int fieldType, int differNum) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(fieldType, differNum);
        return cal.getTime();
    }

    /**
     * 将Date时间转换成LocalDateTime时间
     *
     * @param date 时间
     * @return 返回Local时间
     */
    private static LocalDateTime dateToLocalDateTime(Date date) {
        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * 输入日期是否闰年
     *
     * @param date 时间
     * @return 返回输入日期是闰年为true，不是false
     */
    public static boolean isLeapYear(Date date) {
        return dateToLocalDateTime(date).toLocalDate().isLeapYear();
    }

    /**
     * 获取某日期是星期几 返回英文星期 eg：monday
     *
     * @param date 时间
     * @return 返回英文的星期几
     */
    public static String dayOfWeekEN(Date date) {
        return dateToLocalDateTime(date).getDayOfWeek().toString();
    }

    /**
     * 获取date该天的开始时间，00:00:00
     *
     * @param date 时间
     * @return 返回该天的开始时间
     */
    public static Date startTimeOfDate(Date date) {
        if (date == null) {
            return null;
        }
        LocalDateTime localDateTime = dateToLocalDateTime(date).toLocalDate().atTime(0, 0, 0);
        return localDateTimeToDate(localDateTime);
    }

    /**
     * 获取date该天的结束时间，23:59:59
     *
     * @param date 时间
     * @return 返回该天的结束时间
     */
    public static Date endTimeOfDate(Date date) {
        if (date == null) {
            return null;
        }
        LocalDateTime localDateTime = dateToLocalDateTime(date).toLocalDate().atTime(23, 59, 59);
        return localDateTimeToDate(localDateTime);
    }

    /**
     * 将本地时间localDateTime转换为时间Date
     *
     * @param localDateTime 本地时间
     * @return Date 时间日期
     */
    private static Date localDateTimeToDate(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * 将时间增加或减去years年
     *
     * @param data
     * @param years
     * @return
     */
    public static Date addYears(Date data, int years) {
        Calendar tempDate = Calendar.getInstance();
        tempDate.setTime(data);
        tempDate.add(Calendar.YEAR, years);
        return tempDate.getTime();
    }

    /**
     * 将时间增加或减去months月
     *
     * @param data
     * @param months
     * @return
     */
    public static Date addMonths(Date data, int months) {
        Calendar tempDate = Calendar.getInstance();
        tempDate.setTime(data);
        tempDate.add(Calendar.MONTH, months);
        return tempDate.getTime();
    }

    /**
     * 将时间增加或减去days天
     *
     * @param data
     * @param days
     * @return
     */
    public static Date addDays(Date data, int days) {
        Calendar tempDate = Calendar.getInstance();
        tempDate.setTime(data);
        tempDate.add(Calendar.DATE, days);
        return tempDate.getTime();
    }

    /**
     * 将时间增加或减去minutes分钟
     *
     * @param data
     * @param minutes
     * @return
     */
    public static Date addMinutes(Date data, int minutes) {
        Calendar tempDate = Calendar.getInstance();
        tempDate.setTime(data);
        tempDate.add(Calendar.MINUTE, minutes);
        return tempDate.getTime();
    }

    /**
     * 将区间时间拆分为每天
     *
     * @param start
     * @param end
     * @return
     */
    public static List<Date> getBetweenDates(Date start, Date end) {
        List<Date> result = new ArrayList<>();
        result.add(start);
        Calendar tempStart = Calendar.getInstance();
        tempStart.setTime(start);
        Calendar tempEnd = Calendar.getInstance();
        tempEnd.setTime(end);
        while (tempStart.before(tempEnd)) {
            tempStart.add(Calendar.DATE, 1);
            result.add(tempStart.getTime());
        }
        return result;
    }

    /**
     * 获取两个时间相差天数
     *
     * @param start
     * @param end
     * @return
     */
    public static long dateTimeDiffDay(Date start, Date end) {
        long dif = end.getTime() - start.getTime();
        return (long) Math.ceil(dif / (double) (24 * 60 * 60 * 1000));
    }

}
